#!/usr/bin/env python
"""
:mod:`pynchon.core.point`
=========================

Provides array objects to store N-dimensional float coordinates.
"""

import numpy as np
from entity import EntityHierarchy, Entity_maker, Entity_sequencer


class Point(object):
    """
    Point interface class. This class provides methods specific to 
    N-dimensional coordinates e.g. 0D - 2.789, 1D - (3.001,), 2D - (1.0, 7.5).
    
    Provides: 'raw' and 'is_valid' methods and the 'rank' attribute to
    subclasses. 
    """
    rank = 0

    @classmethod
    def is_valid_element(cls, element, metadata):
        """
        Verify if element can be a valid ``Point`` instance.
        """
        try:
            if not np.isfinite(element).all():
                raise ValueError("Not all elements of \"%s\" are finite." % \
                                 (element,))
        except (AttributeError, TypeError):
            raise ValueError("Expected a sequence of floats got: \"%s\"." % \
                             (element,))

    @classmethod
    def raw_sequence(cls, rank, dtype, fval, metadata, fast, kwargs, entities):
        cls.is_valid_element(entities, metadata)
        try:
            entities = np.array(entities, dtype=dtype)
        except (ValueError, TypeError):
            raise ValueError("Expected a %sD sequence of floats got: \"%s\"." \
                             % (len(dtype), entities))
        cls.is_valid_array(entities, metadata)
        cls.is_valid_array(fval, metadata)
        return (entities,)
    
    def asarray(self):
        """
        """
        ln = len(self.dtype)
        dt = self.dtype[0] if ln else self.dtype
        sh = (ln,) if ln else ()
        plain = self[None].view(type=np.ndarray, dtype=dt)
        return plain.reshape(sh)
    
    def __sub__(self, y): 
        x = self.asarray()
        y = y.asarray() if hasattr(y, 'asarray') else y
        res = x - y
        return res.view(type=self.__class__, dtype=self.data_type).reshape(self.shape)

    def __add__(self, y):
        x = self.asarray()
        y = y.asarray() if hasattr(y, 'asarray') else y
        res = x + y
        return res.view(type=self.__class__, dtype=self.data_type).reshape(self.shape)
    
    def __mul__(self, y):
        x = self.asarray()
        y = y.asarray() if hasattr(y, 'asarray') else y
        res = x * y
        return res.view(type=self.__class__, dtype=self.data_type).reshape(self.shape) 
    
    def __div__(self, y):
        x = self.asarray()
        y = y.asarray() if hasattr(y, 'asarray') else y
        res = x / y
        return res.view(type=self.__class__, dtype=self.data_type).reshape(self.shape) 
    
           
class PointVector(Point):
    """
    PointVector interface class.This class provides methods specific to
    vectors of N-dimensional coordinates e.g. 0D - [1., 0.2, ... , -2.01], 1D - 
    [(3.0,), (2.1,), ... (3.01,)], 2D - [(1.98, 0.0), (2., 3.), ... , (1., 7.)].
    """
    rank = 1

    @classmethod
    def raw_sequence(cls, rank, dtype, fval, metadata, fast, kwargs, entities):
        return Entity_sequencer(cls, dtype, fval, metadata, entities)

    def get_indices(self):
        """
        """
    def asarray(self):
        """
        """
        ln = len(self.dtype)
        dt = self.dtype[0] if ln else self.dtype
        sh = self.shape + ((ln,) if ln else ())
        plain = self.view(type=np.ndarray, dtype=dt)
        return plain.reshape(sh)


class PointArray(PointVector):
    """
    PointArray interface class. This class provides methods specific to
    arrays of N-dimensional points.
    """
    rank = 2


class PointCube(PointArray):
    """
    PointCube interface class. This class provides methods specific to
    cubes (3D) of N-dimensional points.
    """
    rank = 3


class PointHyperCube(PointCube):
    """
    PointHyperCube interface class. This class provides methods specific to
    cubes (4D) of N-dimensional points.
    """
    rank = 4


# create classes
classes = Entity_maker(base_classes=(Point, PointVector, PointArray, \
                                       PointCube, PointHyperCube),
                        dims=(0, 1, 2, 3, 4),
                        types=('float64', 'float32')
                       )
# add to globals
for cls in classes:
    globals()[cls.__name__] = cls











